package com.zzyl.utils;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.zzyl.config.WeChatProperties;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import org.apache.http.HttpHeaders;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Base64;

/**
 * 微信支付工具类
 *
 * @author itcast
 */
@Component
public class WeChatPayUtil {

    //微信支付下单接口地址
    public static final String JSAPI = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";

    //申请退款接口地址
    public static final String REFUNDS = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
    //关闭交易单
    public static final String CLOSE_TRADING = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{}/close";
    //查询退款记录
    public static final String QUERY_REFUND = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/";
    //查询交易单
    public static final String QUERY_TRADING = " https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/";

    @Autowired
    private WeChatProperties weChatProperties;

    /**
     * 获取调用微信接口的客户端工具对象
     *
     * @return
     */
    private CloseableHttpClient getClient() {
        PrivateKey merchantPrivateKey;
        try {
            //merchantPrivateKey商户API私钥，如何加载商户API私钥请看常见问题
            merchantPrivateKey = PemUtil
                    .loadPrivateKey(new ByteArrayInputStream(weChatProperties.getPrivateKey().getBytes(StandardCharsets.UTF_8)));

            // 获取证书管理器实例
            CertificatesManager certificatesManager = CertificatesManager.getInstance();
            // 向证书管理器增加需要自动更新平台证书的商户信息

            certificatesManager.putMerchant(weChatProperties.getMchId(),
                    new WechatPay2Credentials(weChatProperties.getMchId(), new PrivateKeySigner(weChatProperties.getMchSerialNo(), merchantPrivateKey)),
                    weChatProperties.getApiV3Key().getBytes(StandardCharsets.UTF_8));
            Verifier verifier = certificatesManager.getVerifier(weChatProperties.getMchId());

            // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签
            return WechatPayHttpClientBuilder.create()
                    .withMerchant(weChatProperties.getMchId(), weChatProperties.getMchSerialNo(), merchantPrivateKey)
                    .withValidator(new WechatPay2Validator(verifier))
                    .build();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 发送post方式请求
     *
     * @param url  请求路径
     * @param body 请求体
     * @return http响应结果
     */
    private String post(String url, String body) throws Exception {
        CloseableHttpClient httpClient = getClient();

        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
        httpPost.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
        httpPost.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo());
        httpPost.setEntity(new StringEntity(body, "UTF-8"));

        CloseableHttpResponse response = httpClient.execute(httpPost);
        try {
            return EntityUtils.toString(response.getEntity());
        } finally {
            httpClient.close();
            response.close();
        }
    }

    /**
     * 发送get方式请求
     *
     * @param url 请求路径
     * @return http响应结果
     */
    private String get(String url) throws Exception {
        CloseableHttpClient httpClient = getClient();

        HttpGet httpGet = new HttpGet(url);
        httpGet.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
        httpGet.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
        httpGet.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo());

        CloseableHttpResponse response = httpClient.execute(httpGet);
        try {
            return EntityUtils.toString(response.getEntity());
        } finally {
            httpClient.close();
            response.close();
        }
    }

    /**
     * jsapi下单
     *
     * @param orderNum    商户订单号
     * @param total       总金额，单位：元
     * @param description 商品描述
     * @param openid      微信用户的openid
     * @return
     */
    public String jsapi(String orderNum, BigDecimal total, String description, String openid) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("appid", weChatProperties.getAppId());
        jsonObject.put("mchid", weChatProperties.getMchId());
        jsonObject.put("description", description);
        jsonObject.put("out_trade_no", orderNum);
        jsonObject.put("notify_url", weChatProperties.getNotifyUrl());

        JSONObject amount = new JSONObject();
        amount.put("total", NumberUtil.mul(NumberUtil.round(total, 2), 100).intValue());
        amount.put("currency", "CNY");

        jsonObject.put("amount", amount);

        JSONObject payer = new JSONObject();
        payer.put("openid", openid);

        jsonObject.put("payer", payer);

        String body = jsonObject.toJSONString();
        String jsonResult = "";
        try {
            jsonResult = post(JSAPI, body);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.APPLY_TRADE_FAIL);
        }
        return jsonResult;
    }

    /***
     *  查询交易结果
     * @param outTradeNo 发起支付传递的交易单号
     * @return 交易结果
     */
    public String queryTrading(String outTradeNo) {
        //请求地址
        String url = QUERY_TRADING + outTradeNo + "?mchid=" + weChatProperties.getMchId();
        try {
            return get(url);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.QUERY_TRADING_FAIL);
        }

    }

    /**
     * 申请退款
     *
     * @param outTradeNo  商户订单号
     * @param outRefundNo 商户退款单号
     * @param refund      退款金额
     * @param total       原订单金额
     * @return 退款结果
     */
    public String refund(String outTradeNo, String outRefundNo, BigDecimal refund, BigDecimal total) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("out_trade_no", outTradeNo);
        jsonObject.put("out_refund_no", outRefundNo);

        JSONObject amount = new JSONObject();
        amount.put("refund", NumberUtil.mul(NumberUtil.round(refund, 2), 100).intValue());
        amount.put("total", NumberUtil.mul(NumberUtil.round(total, 2), 100).intValue());
        amount.put("currency", "CNY");
        jsonObject.put("amount", amount);

        String body = jsonObject.toJSONString();

        //调用申请退款接口
        try {
            return post(REFUNDS, body);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.APPLY_REFUND_FAIL);
        }
    }

    /***
     *  统一收单交易退款接口查询
     * @param outRefundNo 商户系统内部的退款单号
     * @return 退款信息
     */
    public String queryRefund(String outRefundNo) {
        String url = QUERY_REFUND + outRefundNo;
        try {
            return get(url);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.QUERY_REFUND_FAIL);
        }
    }

    /**
     * 关闭交易单
     *
     * @param outTradeNo 商户订单号
     * @return http响应结果
     */
    public String closeTrading(String outTradeNo) throws Exception {
        //请求地址
        String url = StrUtil.format(CLOSE_TRADING, outTradeNo);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("mchid", weChatProperties.getMchId());

        String body = jsonObject.toJSONString();
        return post(url, body);
    }

    /**
     * 生成
     *
     * @param timeStamp 时间戳
     * @param nonceStr  随机数
     * @param packages  预支付字符串
     * @return 签名字符串
     * @throws Exception 不处理异常，全部抛出
     */
    public String createPaySign(Long timeStamp, String nonceStr, String packages) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");

        // 加载商户私钥
        PrivateKey privateKey = PemUtil
                .loadPrivateKey(new ByteArrayInputStream(weChatProperties.getPrivateKey().getBytes(CharsetUtil.CHARSET_UTF_8)));
        sign.initSign(privateKey);
        String message = StrUtil.format("{}\n{}\n{}\n{}\n",
                weChatProperties.getAppId(),
                timeStamp,
                nonceStr,
                packages);
        sign.update(message.getBytes());
        return Base64.getEncoder().encodeToString(sign.sign());
    }
}
